package helper
import nci60.data.connected.LeadingProtein
import nci60.data.protein.Protein
import nci60.query.Suggestion
import nci60.data.protein.GeneName
import nci60.data.maxquant.Project
import nci60.helper.algorithms.*
import org.apache.commons.math3.stat.inference.TTest
import org.apache.commons.math3.stat.inference.OneWayAnova
import java.text.DecimalFormat

/**
 * GRAILS <a href="http://grails.org/doc/latest/guide/single.html#services" target="_blank">Services</a> 
 * This service contains methods/closures, which perform shared tasks for searching proteins and global expression analysis. 
 *
 * @autor <a href="mailto:Florian.J.Auer@googlemail.com">Florian J. Auer</a>
*/
class QueryService {

    /**
    * GRAILS <a href="http://grails.org/doc/latest/guide/single.html#declarativeTransactions" target="_blank">Transactional</a>
    * Disables automatic transaction demarcation.
    */
    static transactional = false

    
    
    /**
     * Transforms a List to a String for querying the database via SQL.<p />
     * The query is sourounded by breakets "(" and ")", 
     * the elements are komma separated and sourounded by ' (marks it as String)
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              list
     * </td><td>        List of elements
     * </td></tr>
     * </table>
     */
    def formatQueryList = {list->
        def result = '('
        list.eachWithIndex{element,index->
            result = ((index!=0)?',':'')+result+'\''+element+'\''
        }
        result = result+')'
        return result
    }
    
    
    
    
    
    /**
     * Returns the cell line data (intensities) for selected tissues and projects <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              cls
     * </td><td>        List of the names of wanted tissues, e.g. ["COLON", "BREAST"]
     * </td></tr>
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              projects
     * </td><td>        List of the ids of wanted projects, e.g. "3" for "Deep Proteomes"
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td>        .[projectId].[ProteinId].[CelllineGroupName].[CelllineId]=Intensity
     * </td></tr>
     * </table>
     */
    Map getData(List cls, List projects){
        //map cell line ids to tissue name
        def expIdToClass = [:]
        Project.executeQuery('select e.id, e.currentClass.name from Experiment e where e.excluded=false and e.currentClass.name in :cls and e.currentProject.id in :proj',[cls:cls, proj:projects]).each{line->
            expIdToClass.put(line[0], line[1])
        }        
        
        //querying the database and save result as linked hash map
        def data = [:]
        Project.executeQuery('select e.protein, e.currentProject.id, e.lfqIntensity, e.experiment.id from ProteinGroupExperiment e where e.protein!=0 and e.experiment.id in :cls and e.lfqIntensity!=0', [cls:expIdToClass.keySet()]).each(){line->
//        Project.executeQuery('select l.protein.id, e.currentProject.id, e.lfqIntensity, e.experiment.id from ProteinGroupExperiment e, LeadingProtein l where l.group=e.group and e.experiment.id in :cls and e.lfqIntensity!=0', [cls:expIdToClass.keySet()]).each(){line->
            def protId = line[0].toLong() 
            def curProject = line[1]
            def clsId = expIdToClass[line[3]]
            def intensity = (line[2]!=0)?Math.log10(line[2]):0.0
            def name = line[3]
            if(!data.containsKey(curProject)){data.put(curProject,[:])}
            if(!data[curProject].containsKey(protId)){data[curProject].put(protId,[:])}
            if(!data[curProject][protId].containsKey(clsId)){data[curProject][protId].put(clsId,[:])}
            data[curProject][protId][clsId].put(name, intensity)
        }
        return data.sort{it.key}
    }

    
 
    /**
     * Generate pValues for cell line data (intensities) <p />
     * P-Values are generated for each protein in each project <p />
     * <b>See also:</b> {@link #getData(List, List)} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *              data
     * </td><td>        .[projectId].[ProteinId].[CelllineGroupName].[CelllineId]=Intensity
     * </td></tr>
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              celllines
     * </td><td>        List of the names of wanted tissues, e.g. ["COLON", "BREAST"]
     * </td></tr>
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              clsCounts
     * </td><td>        .[projectId].[CelllineGroupName]=NrOfCelllinesInGroup
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td>        .[projectId].[ProteinId]=pValue
     * </td></tr>
     * </table>
     */
    Map generatePValues(Map data, List celllines, Map clsCounts){
        def pValues = [:]
        data.each{proj, prots->
            if(!pValues.containsKey(proj)){pValues.put(proj, [:])}
            prots.sort{it.key}.each{prot,clss->
                def tmp_intensities = []    //storing the intensities of the cell lines of one tissue as List of double[]
                celllines.each{cls->
                    def tmpIntens = (clss.containsKey(cls))?clss[cls].values() as List:[]
                    while(tmpIntens.size()<clsCounts[proj][cls]){tmpIntens.add(0.0)}    //adding zero values to each tissue, until the number equals the number of cell lines in this tissue 
                    tmp_intensities.add(tmpIntens as double[])
                }
                
                //generating pValue for one protein and project
                def pV = pValue(tmp_intensities)
                if(pV!=Double.NaN){pValues[proj].put(prot.toString(), pV)}  //only not NaN values are saved
            }
        }
        return pValues
    }
    
    
    
    /**
     * Corrected p-values for multiple testing (q-values) <p />
     * Q-values are generated for each protein in each project <p />
     * <b>See also:</b> {@link #generatePValues(Map, List, Map)} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *              pValues
     * </td><td>        .[projectId].[ProteinId]=pValue <p />
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td>        .[projectId].[proteinId]=qValue
     * </td></tr>
     * </table>
     */
    Map generateQValues(Map pValues){
        def qValues = [:]
        pValues.each{k,v->
            def tmp = this.qValues(v, 'BH')     //generating qValues for a Map of protein ids to pValues
            qValues.put(k, tmp)
        }
        return qValues
    }

    
    /**
     * Returns the suggestions for a query (for select2)<p />
     * <b>See also:</b> {@link nci60.query.QueryController#suggest} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              query
     * </td><td>        letters typed in by the user; at least 3 (specified in select2 via JavaScript)
     * </td></tr>
     * <tr><td class="z">
     *              Ineger
     * </td><td class="z">
     *              max
     * </td><td>        maximal length of the returned list (not including the elements like 'starts with' or 'contains')
     * </td></tr>
     * <tr><td class="z">
     *              Integer
     * </td><td class="z">
     *              offset
     * </td><td>        offset of the list; iff offset is 0 general search patterns ('starts with' and 'contains') are added
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *                      .elements
     * </td><td>                .type=
     * </td><td class="z">            type of the element; <p />
     *                                used for defining the class of the element in the list as well as if selected <p />
     *                                'gene' for gene name, 'uniprot' for protein name, 'go' for GO, 'ipi' for IPI identifier, 'description' for protein description         
     * </td></tr>
     * <tr><td class="z">
     * </td><td>                .name=
     * </td><td class="z">            name of the elemnet; shown in list and if selected     
     * </td></tr>
     * <tr><td class="z">
     * </td><td>                .id=
     * </td><td class="z">            String used for defining the query <p />
     *                                is sent by clicking the submit button; 
     *                                format: &lt;type&gt;[Like[S]{0,1}]{0,1}(&lt;query&gt;)[,&lt;type&gt;[Like[S]{0,1}]{0,1}(&lt;query&gt;)]{*} 
     *                                <hide style="display:none;">    format:<type>[Like[S]{0,1}]{0,1}(<query>)[,<type>[Like[S]{0,1}]{0,1}(<query>)]{*}   </hide>
     * </td></tr>
     * <tr><td class="z">
     * </td><td>                .full=
     * </td><td class="z">            is shown as a description of the element; e.g. "5 Occureances";        
     * </td></tr>
     * <tr><td class="z">
     *                      .total=
     * </td><td colspan="2">          total number of suggestions; <p />
     *                                necessary for select2 to know if there are more elements and therefore load them when scroll bar reaches the bottom
     * </td></tr>
     * </table>
     */
    Map getSuggestions(String query, int max, int offset){
        def result = []
        def total = 0
        def counter = 0
        def currOffset = 0
        //get all phrases starting, ending or containing the suggested letters
        def exeQuery = 'select s.phrase, s.type, count(s.id) from Suggestion s where s.phrase like \'%'+query+'%\' or s.phrase like \''+query+'%\' or s.phrase like \'%'+query+'\' group by s.type, s.phrase'
        def suggestions = Suggestion.executeQuery(exeQuery).sort{it[0].size()}

        if(suggestions.size()!=0){  //at least one suggestion is found
            def counts = [gene:0, uniprot:0, go:0]
            suggestions.each{if(counts.containsKey(it[1])){counts[it[1]]++}}    //count gene names, protein names and go terms matching the suggested letters
            if(offset==0){          //first suggestion offers also the possibility to select groups like gene starting with or containing the suggested letters
                if(counts.gene>0){      //for genes
                    result.add(['type':'gene', 'name':query.toUpperCase()+'..', 'id':'geneLikeS('+query.toUpperCase()+')', 'full':'all Genes starting with \"'+query.toUpperCase()+'\"'])
                    result.add(['type':'gene', 'name':'..'+query.toUpperCase()+'..', 'id':'geneLike('+query.toUpperCase()+')', 'full':'all Genes containing \"'+query.toUpperCase()+'\"'])
                }
                if(counts.uniprot>0){   //for uniprot protein names
                    result.add(['type':'uniprot', 'name':query.toUpperCase()+'..', 'id':'uniprotLikeS('+query.toUpperCase()+')', 'full':'all Protein Names starting with \"'+query.toUpperCase()+'\"'])
                    result.add(['type':'uniprot', 'name':'..'+query.toUpperCase()+'..', 'id':'uniprotLike('+query.toUpperCase()+')', 'full':'all Protein Names containing \"'+query.toUpperCase()+'\"'])
                }
                if(counts.go>0){        //for go terms; only containing
                    result.add(['type':'go', 'name':'..'+query.toUpperCase()+'..', 'id':'goLike('+query.toUpperCase()+')', 'full':'all Gene Ontology Terms containing \"'+query.toUpperCase()+'\"'])
                }
            }
            
            //pagination of the List of suggestions
            def to = Math.min((offset+max), suggestions.size())-1
            if(offset<to){
                suggestions[offset..to].each(){sugg->
                    result.add(['type':sugg[1], 'name':sugg[0], 'id':sugg[1]+'('+sugg[0]+')', 'full':sugg[2]+' Occurances'])
                }
                total += suggestions.size()
            }else if(offset==to){
                def sugg = suggestions[to]
                result.add(['type':sugg[1], 'name':sugg[0], 'id':sugg[1]+'('+sugg[0]+')', 'full':sugg[2]+' Occurances'])
            }
        }

        //checking if protein identifier contains the suggested letters, whith pagination and adding it to the results
        LeadingProtein.executeQuery('select distinct l.protein from LeadingProtein l where l.protein.mainId like \'%'+query+'%\'', [max:max, offset:offset]).each(){i->
            def element = ['type':'ipi', 'name':i.mainId, 'id':('protein('+i.mainId+')')]
            def temp = (i.description.size()>30) ? i.description[0..27]+'...' : i.description
            element.put('full',temp)
            result.add(element)
        }
        //counting all protein identifier containing the suggested letters
        def nrOfProteinIds = Protein.executeQuery('select count(distinct l.protein) from LeadingProtein l where l.protein.mainId like \'%'+query+'%\'')[0]
        
        total = Math.max(total, nrOfProteinIds)
        return ['elements':result, 'total':total]
    }

    
    /**
     * Returns the protein ids matching the query from select2<p />
     * <b>See also:</b> {@link #getSuggestions(String, int, int)} <p />
     * <b>See also:</b> {@link nci60.query.QueryController#suggest} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              query
     * </td><td>        query from select2 <p />
     *                  format: &lt;type&gt;[Like[S]{0,1}]{0,1}(&lt;query&gt;)[,&lt;type&gt;[Like[S]{0,1}]{0,1}(&lt;query&gt;)]{*} 
     *                  <hide style="display:none;">    format:<type>[Like[S]{0,1}]{0,1}(<query>)[,<type>[Like[S]{0,1}]{0,1}(<query>)]{*}   </hide>
     * </td></tr>
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              projects
     * </td><td>        List of project ids the proteins have to be identified in; <p />
     *                  if null all projects are used; if empty list no proteins are returned
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * List of protein ids
     * </table>
     */
    List getProteinIdsFromQuery(String query, List projects){
        //if projects is null, in all projects is searched; if projects is empty list no protein ids are returned imediatly
        if((projects==null)){
            projects = []
            Project.findAll().each{proj->projects.add(proj.id)}
        }else if(projects.size()==0){
            return []
        }
        
        def protIds = [:]
        if(query!=''){      //only if a query is specified; else all protein ids are returned
            def proteins = []
            def descriptions = []
            def genes = []
            def genesLike = []
            def gos = []
            def gosLike = []
            def uniprots = []
            def uniprotsLike = []
            //each element of the query is done individually
            query.split(',').eachWithIndex(){col, i->
                //preparing the matcher
                def protMatch = (col =~  /protein\(([A-Z0-9]+)\)/)      //its a protein identifier
                def descMatch = (col =~  /description\((.+)\)/)         //its a protein description
                def unipMatch = (col =~  /uniprot\((.+)\)/)             //its a uniprot protein name
                def unipLikeMatch = (col =~  /uniprotLike\((.+)\)/)     //its a uniprot protein name containing that
                def unipLikeSMatch = (col =~  /uniprotLikeS\((.+)\)/)   //its a uniprot protein name starting with that
                def geneMatch = (col =~  /gene\((.+)\)/)                //its a gene name
                def geneLikeMatch = (col =~  /geneLike\((.+)\)/)        //its a gene name containing that
                def geneLikeSMatch = (col =~  /geneLikeS\((.+)\)/)      //its a gene name starting with that
                def goMatch = (col =~  /go\((.+)\)/)                    //its a go term
                def goLikeMatch = (col =~  /goLike\((.+)\)/)            //its a go term containing that
                
                //check which matcher is matching
                if(protMatch.matches()){
                    proteins.add(protMatch[0][1])
                }else if(descMatch.matches()){
//                    println 'desc match'
                    descriptions.add(descMatch[0][1])
                }else if(goMatch.matches()){
                    gos.add(goMatch[0][1])
                }else if(goLikeMatch.matches()){
                    gosLike.add('%'+goLikeMatch[0][1]+'%')
                }else if(geneMatch.matches()){
                    genes.add(geneMatch[0][1])
                }else if(geneLikeMatch.matches()){
                    genesLike.add('%'+geneLikeMatch[0][1]+'%')
                }else if(geneLikeSMatch.matches()){
                    genesLike.add(geneLikeSMatch[0][1]+'%')
                }else if(unipMatch.matches()){
                    uniprots.add(unipMatch[0][1])
                }else if(unipLikeMatch.matches()){
                    uniprotsLike.add('%'+unipLikeMatch[0][1]+'%')
                }else if(unipLikeSMatch.matches()){
                    uniprotsLike.add(unipLikeSMatch[0][1]+'%')
                }
            }
            
            //preparing the querys for querying the database, and storing it as List
            def suggestionQueries = []
            if(descriptions.size()>0){
                suggestionQueries.add(' (s.phrase in '+formatQueryList(descriptions)+' and s.type=\'description\')')
            }
            if(gos.size()>0){
                suggestionQueries.add(' (s.phrase in '+formatQueryList(gos)+' and s.type=\'go\')')
            }
            if(gosLike.size()>0){
                gosLike.each{g->
                    suggestionQueries.add(' (s.phrase like \''+g+'\' and s.type=\'go\')')
                }
            }
            if(genes.size()>0){
                suggestionQueries.add(' (s.phrase in '+formatQueryList(genes)+' and s.type=\'gene\')')
            }
            if(genesLike.size()>0){
                genesLike.each{g->
                    suggestionQueries.add(' (s.phrase like \''+g+'\' and s.type=\'gene\')')
                }
            }
            if(uniprots.size()>0){
                suggestionQueries.add(' (s.phrase in '+formatQueryList(uniprots)+' and s.type=\'uniprot\')')
            }
            if(uniprotsLike.size()>0){
                uniprotsLike.each{u->
                    suggestionQueries.add(' (s.phrase like \''+u+'\' and s.type=\'uniprot\')')
                }
            }
            
            //if there are querys generated
            if(suggestionQueries.size()>0){
                //connecting the single querys to one big
                def suggestionQuery = ''
                suggestionQueries.eachWithIndex(){q,index->
                    if(index==0){
                        suggestionQuery = q
                    }else{
                        suggestionQuery = suggestionQuery+' or '+q
                    }
                }
                
                //perform the query and store the protein ids
                Suggestion.executeQuery('select distinct l.protein.id from Suggestion s, LeadingProtein l where l.protein=s.protein and l.currentProject.id in :proj and l.group.intensity!=0 and ('+suggestionQuery+')', [proj:projects]).each(){
                    protIds.put(it.toLong(), 0)
                }
            }
            
            //filter out the ids of proteins which are not identified
            if(proteins.size()>0){
                Protein.executeQuery('select distinct l.protein.id from LeadingProtein l where l.protein.mainId in :prots and l.currentProject.id in :proj and l.group.intensity!=0', [prots:proteins, proj:projects]).each(){
                    protIds.put(it.toLong(), 0)
                }
            }
        }else{      //query empty: returns all protein ids
            Protein.executeQuery('select distinct l.protein.id from LeadingProtein l where l.currentProject.id in :proj and l.group.intensity!=0', [proj:projects]).each(){
                protIds.put(it.toLong(), 0)
            }
        }
        
        //return the protein ids
        return protIds.keySet().toList()
    }

    
    
    /**
     * Returns the proteins matching the query from select2<p />
     * <b>See also:</b> {@link nci60.query.QueryController#suggest} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              query
     * </td><td>        query from select2 <p />
     *                  format: &lt;type&gt;[Like[S]{0,1}]{0,1}(&lt;query&gt;)[,&lt;type&gt;[Like[S]{0,1}]{0,1}(&lt;query&gt;)]{*} 
     *                  <hide style="display:none;">    format:<type>[Like[S]{0,1}]{0,1}(<query>)[,<type>[Like[S]{0,1}]{0,1}(<query>)]{*}   </hide>
     * </td></tr>
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              projects
     * </td><td>        List of project ids the proteins have to be identified in; <p />
     *                  if null all projects are used; if empty list no proteins are returned
     * </td></tr>
     * <tr><td class="z">
     *              Ineger
     * </td><td class="z">
     *              max
     * </td><td>        maximal number of returned protein ids (pagination)
     * </td></tr>
     * <tr><td class="z">
     *              Integer
     * </td><td class="z">
     *              offset
     * </td><td>        offset of the list (pagination)
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">   .proteins
     * </td><td>                .[proteinId]
     * </td><td class="z">            .mainId=
     * </td><td colspan="4">                IPI protein identifier
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .description=
     * </td><td colspan="4">                description of the protein
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .gene=
     * </td><td colspan="4">                gene name of the protein
     * </td></tr>   
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .cls
     * </td><td>                            .[cellLineGroup]=List    
     * </td><td class="z">                          .project=
     * </td><td colspan="2">                                project id in the List of tissues
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">           
     * </td><td>                               
     * </td><td class="z">                          .intensity=
     * </td><td colspan="2">                                project id in the List of tissues
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .max=
     * </td><td colspan="4">                maximal intensity value of a protein (Integer)
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .projectMax=
     * </td><td>                            .[projectId]=
     * </td><td class="z" colspan="3">             maximal intensity in each project
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .nrOfCelllines
     * </td><td>                            .all
     * </td><td class="z">                          .[cellLineGroup]
     * </td><td>                                            =0        
     * </td><td class="z">                                          number of tissues in this protein; <p />
     *                                                              will be used as "...all.size()" to get the number of cell line groups (tissues)
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .nrOfBars=
     * </td><td colspan="4">                number of all cell lines of this project (equals the bars in the little histogram at the protein search page)
     * </td></tr>
     * <tr><td class="z">   .nrOfProteins
     * </td><td colspan="6">     number of total found proteins    
     * </td></tr>
     * </table>
     */
    Map getProteinsFromQuery(String query, List projects, int max, int offset){
        //get all protein ids
        def protIds = getProteinIdsFromQuery(query, projects)
        def nrOfProteins = protIds.size()
        def proteins = [:]

        //pagination
        def start = max*offset
        if(start<protIds.size()){
            def end = ((start+max)>=protIds.size())?protIds.size()-1:start+max-1
            protIds = protIds[start..end]

            //get protein information
            Protein.executeQuery('select p.id, p.mainId, p.description from Protein p where p.id in :prots', [prots:protIds]).each{line->
                def id = line[0]
                def mainId = line[1]
                def description = line[2]
                proteins.put(id, [mainId:mainId, description:description, gene:null, cls:[:], max:0, nrOfCelllines:[all:[:], projects:[:]], nrOfBars:0, projectMax:[:]])
            }
            
            //get gene names
            GeneName.executeQuery('select g.protein.id, g.name from GeneName g where g.protein.id in :prots and g.isMainName=true', [prots:protIds]).each{line->
                def prot = line[0]
                def gene = line[1]
                proteins[prot].gene=((proteins[prot].gene==null)||(proteins[prot].gene.size()>gene.size()))?gene:proteins[prot].gene
            }
            
            //get intensities and add some iformation for displaying it as histogram
            Protein.executeQuery('select l.protein.id, e.currentProject.id, e.experiment.currentClass.name, e.experiment.name, e.lfqIntensity from LeadingProtein l, ProteinGroupExperiment e where e.group=l.group and e.lfqIntensity!=0 and l.protein.id in :prots and l.currentProject.id in :proj', [prots:protIds, proj:projects]).each{line->
                def prot = line[0]
                def proj = line[1]
                def cls = line[2]
                def cel = line[3]
                def intens = line[4]
                if(!proteins[prot].cls.containsKey(cls)){proteins[prot].cls.put(cls, [])}
                proteins[prot].cls[cls].add([project:proj, intensity:intens])
                if(!proteins[prot].projectMax.containsKey(proj)){proteins[prot].projectMax.put(proj, 0)}
                proteins[prot].projectMax[proj] = (proteins[prot].projectMax[proj]>intens)?proteins[prot].projectMax[proj]:intens
                proteins[prot].max = (proteins[prot].max>intens)?proteins[prot].max:intens
                proteins[prot].nrOfCelllines.all.put(cel,0)
                proteins[prot].nrOfBars++
            }

        }
        
        return [proteins:proteins, nrOfProteins:nrOfProteins]
    }
    
    
    /**
     * Returns all the proteins matching the query from select2 (for downloading the queried proteins)<p />
     * <b>See also:</b> {@link nci60.query.QueryController#proteinDownload} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              query
     * </td><td>        query from select2 <p />
     *                  format: &lt;type&gt;[Like[S]{0,1}]{0,1}(&lt;query&gt;)[,&lt;type&gt;[Like[S]{0,1}]{0,1}(&lt;query&gt;)]{*} 
     *                  <hide style="display:none;">    format:<type>[Like[S]{0,1}]{0,1}(<query>)[,<type>[Like[S]{0,1}]{0,1}(<query>)]{*}   </hide>
     * </td></tr>
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              projects
     * </td><td>        List of project ids the proteins have to be identified in; <p />
     *                  if null all projects are used; if empty list no proteins are returned
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">   .proteins
     * </td><td>                .[proteinId]
     * </td><td class="z">            .mainId=
     * </td><td colspan="3">                IPI protein identifier
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .description=
     * </td><td colspan="3">                description of the protein
     * </td></tr>
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .gene=
     * </td><td colspan="3">                gene name of the protein
     * </td></tr>   
     * <tr><td class="z">   
     * </td><td>                
     * </td><td class="z">            .projects
     * </td><td>                            .[projectId]   
     * </td><td class="z">                          .[cellline]=
     * </td><td>                                            intensities of cell lines of this project
     * </td></tr>
     * </table>
     */
    Map getAllProteinsFromQuery(String query, List projects){
        //get all protein ids
        def protIds = getProteinIdsFromQuery(query, projects)
        def proteins = [:]
        
        //get protein information
        Protein.executeQuery('select p.id, p.mainId, p.description from Protein p where p.id in :prots', [prots:protIds]).each{line->
            def id = line[0]
            def mainId = line[1]
            def description = line[2]
            proteins.put(id, [mainId:mainId, description:description, genes:[], projects:[:]])
        }
        //get gene names
        GeneName.executeQuery('select g.protein.id, g.name from GeneName g where g.protein.id in :prots order by g.protein.id, g.isMainName', [prots:protIds]).each{line->
            def prot = line[0]
            def gene = line[1]
            proteins[prot].genes.add(gene)
        }
        
        //cell lines and intensities
        Protein.executeQuery('select l.protein.id, e.currentProject.id, e.experiment.id, e.lfqIntensity from LeadingProtein l, ProteinGroupExperiment e where e.group=l.group and e.lfqIntensity!=0 and l.protein.id in :prots and l.currentProject.id in :proj', [prots:protIds, proj:projects]).each{line->
            def prot = line[0]
            def proj = line[1]
            def cel = line[2]
            def intens = line[3]
            if(!proteins[prot].projects.containsKey(proj)){proteins[prot].projects.put(proj, [:])}
            proteins[prot].projects[proj].put(cel, intens)
        }
        
        return proteins
    }
    
    
    /**
     * Computes the p-value for a set of data (one protein in one project)<p />
     * if no or only one value is given, null is returned <p />
     * if only two tissues are give, TTest is performed <p />
     * if three or more values are given, anova is performed <p />
     * <b>See also:</b> {@link #generatePValues(Map, List, Map)} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              Double[]
     * </td><td>        List of double[] containing intensities. <p />
     *                  The List contains an double[] for each cell line group.
     *                  Each double[] contains intensities belonging to one cell line group (tissue), 
     *                  i.e. one intensity for each cell line in that group. 
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Double
     * </td><td>        p-value for that set of data, i.e. that protein
     * </td></tr>
     * </table>
     */
    double pValue(List data){
        def pValue = null

        if(data.size()==2){             //only two tissues, therefore TTest is performed
            TTest anova = new TTest()
            pValue = anova.homoscedasticTTest(data[0], data[1])
        }else if(data.size()>2){        //three or more tissues, therefore Anova is performed
            OneWayAnova anova = new OneWayAnova()
            pValue = anova.anovaPValue(data)
        }

        return pValue
    }

    
    /**
     * Computes the q-values for a list of p-values <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *                  PValues
     * </td><td>            .[proteinId]=pValue <p/ >
     *                      proteinId:String, pValue:Double  <p/ >
     *                      proteinId has to be String because of the used correction implementation
     * </td></tr>
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *                  algo
     * </td><td>            algorithm used for generation qValues; <p />
     *                      'BH' stands for BenjaminiHochberg; <p />
     *                      'BF' stands for Bonferroni
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              .[proteinId]=
     * </td><td>        p-value for each protein
     * </td></tr>
     * </table>
     */
    Map qValues(Map PValues, String algo) {
        def mapOfQValues = [:]
        if(algo=='BH'){     //using BenjaminiHochberg correction
            def test = new BenjaminiHochbergFDR(PValues, "0.0")     //setting up
            test.run()                                              //perform correction
            def labels = test.getOrdenedGOLabels()                  //get List of protein ids back
            def qValues = test.getAdjustedPvalues()                 //get List of qValues back

            //put the result in a Map
            labels.eachWithIndex(){label,i->
                mapOfQValues.put(label,qValues[i].toDouble())
            }
        }else if(algo=='BF'){   //using Bonferroni correction
            def test = new Bonferroni(PValues, "0.0")               //setting up
            test.run()                                              //perform correction
            def labels = test.getOrdenedGOLabels()                  //get List of protein ids back
            def qValues = test.getAdjustedPvalues()                 //get List of qValues back

            //put the result in a Map
            labels.eachWithIndex(){label,i->
                mapOfQValues.put(label,qValues[i].toDouble())
            }
        }
        return mapOfQValues
    }
    
    
    /**
     * Filter cell line data by queried proteins. <p />
     * This contains the reference cell line, if up, down or non regulated <p />
     * <b>See also:</b> {@link nci60.query.QueryController#globalExpressionResults} <p />
     * <b>See also:</b> {@link nci60.query.QueryController#globalExpressionDownload} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *                  data
     * </td><td>            .[projectId]
     * </td><td class="z">      .[ProteinId]
     * </td><td>                    .[CelllineGroup]
     * </td><td class="z">              .[CelllineId]=
     * </td><td>                            Intensity of the cell line
     * </td></tr>
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *                  proteinIds
     * </td><td colspan="5"> List of protein ids matching the query
     * </td></tr>
     * <tr><td class="z">
     *              Map
     * </td><td class="z">
     *                  qValues
     * </td><td>            .[projectId]
     * </td><td class="z">      .[ProteinId]=
     * </td><td colspan="3">         qValues of the proteins in each project
     * </td></tr>
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *                  refLine     
     * </td><td colspan="5"> cell line group name (tissue) used as reference; <p />
     *                       'avg' if the average over all intensities is used
     * </td></tr>
     * <tr><td class="z">
     *              Boolean
     * </td><td class="z">
     *                  up
     * </td><td colspan="5"> true if up-regulated tissues should be shown
     * </td></tr>
     * <tr><td class="z">
     *              Boolean
     * </td><td class="z">
     *                  non
     * </td><td colspan="5"> true if non-regulated tissues should be shown
     * </td></tr>
     * <tr><td class="z">
     *              Boolean
     * </td><td class="z">
     *                  down
     * </td><td colspan="5"> true if down-regulated tissues should be shown
     * </td></tr>
     * </table><p />
     * <p />
     *  
     * Return: <p />
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">           .regulation=List
     * </td><td>                        .protein=
     * </td><td class="z" colspan="4">      Protein id of this entry in the List
     * </td></tr>
     * <tr><td class="z">
     * </td><td>                        .cls
     * </td><td class="z">                  .[cls]
     * </td><td>                                .up
     * </td><td class="z">                          Number of projects this tissue (cls) is up-regulated in
     * </td></tr>
     * <tr><td class="z">
     * </td><td>        
     * </td><td class="z">    
     * </td><td>                                .down
     * </td><td class="z">                          Number of projects this tissue (cls) is down-regulated in
     * </td></tr>
     * <tr><td class="z">
     * </td><td>           
     * </td><td class="z">     
     * </td><td>                                .non
     * </td><td class="z">                          Number of projects this tissue (cls) is non-regulated in
     * </td></tr>
     * <tr><td class="z">
     * </td><td>                        .pValue
     * </td><td class="z" colspan="3">      Minimal pValue of one used project in this protein in the List
     * </td></tr>
     * <tr><td class="z">
     * </td><td>                        .projPValues
     * </td><td class="z">                  .[projectId]=
     * </td><td colspan="2">                    pValues of the projects of this protein in the List
     * </td></tr>
     * <tr><td class="z">           .nrOfFoundProteins
     * </td><td>                        .[projectId]=
     * </td><td class="z" colspan="3">      Number of found proteins per project
     * </td></tr>
     * </table>
     */
    Map filterData(Map data, List proteinIds, Map qValues, String refLine, boolean up, boolean non, boolean down){
        //at least two CelllineGroups (tissues) are necessary for generating pValues and comparison (up/down/non regulated)
        def minClsSize = 2
        
        //filteredData[ProteinId][ProjectId][CelllineGroup]=AverageOfCelllineGroup
        def filteredData = [:]
        //avgExtraAvgs[ProteinId][ProjectId]=AverageOverAllCelllineGroups; only generated and used iff Reference is Average
        def avgExtraAvgs = [:]
        
        //filter data for proteins with not enough tissues, generating averages (per tissue and per protein) and containing data for the reference tissue
        data.each{proj,prots->
            prots.each{prot,clss->
                //filter the data for wanted (queried) Proteins with at least minClsSize CelllineGroups and containing the Reference CelllineGroup if not Average is used
                if(((refLine=='avg')||(clss.containsKey(refLine)))&&(prot.toLong() in proteinIds)&&(clss.size()>=minClsSize)){
                    if(!filteredData.containsKey(prot)){filteredData.put(prot, [:])}
                    if(!filteredData[prot].containsKey(proj)){ filteredData[prot].put(proj,[:]) }
                    def tmp_sumOfAllIntensities = 0
                    def tmp_nrOfAllIntensities = 0
                    clss.each{cls,intensities->
                        def tmp_sumOfIntensities = 0
                        intensities.values().each{tmp_sumOfIntensities = tmp_sumOfIntensities + it}
                        if(refLine=='avg'){
                            tmp_sumOfAllIntensities = tmp_sumOfAllIntensities + tmp_sumOfIntensities
                            tmp_nrOfAllIntensities = tmp_nrOfAllIntensities + intensities.size()
                        }
                        filteredData[prot][proj].put(cls,(tmp_sumOfIntensities/intensities.size()))
                    }
                    if(refLine=='avg'){
                        if(!avgExtraAvgs.containsKey(prot)){avgExtraAvgs.put(prot, [:])}
                        avgExtraAvgs[prot].put(proj, (tmp_sumOfAllIntensities/tmp_nrOfAllIntensities))
                    }
                }
            }
        }
        
        DecimalFormat df = new DecimalFormat( "0.00E0" )
        
        //regulation[]=.protein=proteinId                 Protein id of this entry in the List
        //             .cls[cls].up=numberOfProjects      Number of projects this tissue (cls) is up-regulated in
        //                      .down=numberOfProjects    Number of projects this tissue (cls) is down-regulated in
        //                      .non=numberOfProjects     Number of projects this tissue (cls) is non-regulated in
        //             .pValue=minimalPValue              Minimal pValue of one used project in this protein in the List
        //             .projPValues[projectId]=qValue     pValues of the projects of this protein in the List
        def regulation = []
        //  nrOfFoundProteins[projectId]=numberOfProteins   Number of found proteins per project
        def nrOfFoundProts = [:]
        
        //filter data for regulation (up/down/non), and therefore pValue, setting regulation class (for css rendering), assign order for sorting entries in list (number of regulated tissues and pValue)
        filteredData.each{prot,projects->
            def tmp_cellLineRegulation = [protein:prot, cls:[:], pValue:1, projPValues:[:]]
            //tmp_chooseProt[projectId]=boolean     temporaly storing the protein ids for each project for later collecting
            def tmp_chooseProt = [:]    
            projects.each{proj,clss->
                if(!nrOfFoundProts.containsKey(proj)){nrOfFoundProts.put(proj, 0)}
                if(!tmp_chooseProt.containsKey(proj)){tmp_chooseProt.put(proj, false)}
                tmp_cellLineRegulation.projPValues.put(proj, qValues[proj][prot.toString()])    //temporary entry of a project
                clss.each{cls,avg->
                    if(!tmp_cellLineRegulation.cls.containsKey(cls)){tmp_cellLineRegulation.cls.put(cls, [up:0, down:0, non:0])}
                    def cellLineReg = tmp_cellLineRegulation.cls[cls]
                    if((cls!=refLine)&&(qValues[proj][prot.toString()]<=0.05)){     //pValue of <= 0.05 means differentially expressed (the reference tissue is always non-regulated)
                        def tmp_reg = (refLine=='avg')?(avg/avgExtraAvgs[prot][proj]):(avg/clss[refLine])   //using reference tissue value or average value for calculating up/down regulation
                        if((tmp_reg>=1)&&(up)){     //tissue is up regulated and up-regulated tissues should be shown 
                            cellLineReg.up++        //counting the regulated tissues of one protein
                            tmp_chooseProt[proj]=true
                            tmp_cellLineRegulation.pValue = (tmp_cellLineRegulation.pValue<=qValues[proj][prot.toString()]) ? tmp_cellLineRegulation.pValue : qValues[proj][prot.toString()]    //updating the minimal pValue for one protein
                        }
                        if((tmp_reg<1)&&(down)){    //tissue is down regulated and down-regulated tissues should be shown 
                            cellLineReg.down++      //counting the regulated tissues of one protein
                            tmp_chooseProt[proj]=true
                            tmp_cellLineRegulation.pValue = (tmp_cellLineRegulation.pValue<=qValues[proj][prot.toString()]) ? tmp_cellLineRegulation.pValue : qValues[proj][prot.toString()]    //updating the minimal pValue for one protein
                        }
                    }else{          //tissue is not differentially expressed(non-regulated) 
                        if(non){    //..and non-regulated tissues should be shown 
                            tmp_chooseProt[proj]=true
                            cellLineReg.non++       //counting the regulated tissues of one protein
                            tmp_cellLineRegulation.pValue = (tmp_cellLineRegulation.pValue<=qValues[proj][prot.toString()]) ? tmp_cellLineRegulation.pValue : qValues[proj][prot.toString()]    //updating the minimal pValue for one protein
                        }
                    }
                }
            }
            def tmp_sum = 0     //used for ordering the proteins in the list; counts the tissues and projects
            //counting tissues and generating regulation class for css rendering the boxes
            tmp_cellLineRegulation.cls.each{cls,values->
                tmp_sum = tmp_sum+values.up+values.down+values.non
                def regClass = 'class'
                regClass = (up&&(values.up>0)) ? regClass+'1' : regClass+'0'
                regClass = (non&&(values.non>0)) ? regClass+'1' : regClass+'0'
                regClass = (down&&(values.down>0)) ? regClass+'1' : regClass+'0'
                values.put('regClass', regClass)
            }
            //assigning the order value to a protein; only proteins with at least one counted tissue in any project are appearing in the result! 
            if(tmp_sum!=0){
                tmp_cellLineRegulation.put('order', (1000-tmp_sum)+tmp_cellLineRegulation.pValue)
                tmp_cellLineRegulation.put('pValue', df.format(tmp_cellLineRegulation.pValue))

                //counting the proteins per project
                tmp_chooseProt.each{proj,choose->
                    if(choose){nrOfFoundProts[proj]++}  
                }
                
                //adding protein to result
                regulation.add(tmp_cellLineRegulation)
            }
        }
        
        return [regulation:regulation, nrOfFoundProteins:nrOfFoundProts]
    }
    
    
    
    /**
     * Transforms a List to a String for a JQuery response.<p />
     * The query is sourounded by breakets "[" and "]", 
     * the elements are komma separated
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              List
     * </td><td class="z">
     *              list
     * </td><td>        List of elements
     * </td></tr>
     * </table>
     */
    def formatJQueryCallList = {list->
        def result = '['
        list.eachWithIndex{element,index->
            result = result+''+element
            if(index!=(list.size-1)){
                result = result+','
            }
        }
        result = result+']'
        return result
    }
    
    
    
    /**
     * Transform a query String from select2 to a String, which can be used in a gsp file to setting up select2 with preset elements<p />
     * <b>See also:</b> {@link nci60.query.QueryController#proteins} <p />
     * <p />
     * 
     * Parameter (params): <p />
     * 
     * <style>.custom-table td{border-top:1pt solid grey;vertical-align:top;} .custom-table{border-collapse:collapse;width:100%;} .custom-table .z{background-color:rgb(230, 230, 230);}</style>
     * <table class="custom-table">
     * <tr><td class="z">
     *              String
     * </td><td class="z">
     *              query
     * </td><td>        Query from select2
     * </td></tr>
     * </table>
     */
    String getJQeryCallFromQuery(String query) {
        def resultArray = []
        query.split(',').each{element->     //splitting up the query in single elements
            def ele = element[0..-2].split('\\(')
            def typeMark = ele[0]
            def value = ele[1]
            def type = null
            
            //check for types
            if(typeMark=='gene'){
                type='gene'
            }else if(typeMark=='uniprot'){
                type='uniprot'
            }else if(typeMark=='go'){
                type='go'
            }else if(typeMark=='geneLike'){
                type='gene'
                value='..'+value+'..'
            }else if(typeMark=='uniprotLike'){
                type='uniprot'
                value='..'+value+'..'
            }else if(typeMark=='goLike'){
                type='go'
                value='..'+value+'..'
            }else if(typeMark=='geneLikeS'){
                type='gene'
                value=value+'..'
            }else if(typeMark=='uniprotLikeS'){
                type='uniprot'
                value=value+'..'
            }else if(typeMark=='description'){
                type='description'
            }else if(typeMark=='protein'){
                type='ipi'
            }
            
            //add element if it is set up
            if(type!=null){resultArray.add('{id: \"'+element+'\", type: \"'+type+'\", name: \"'+value+'\"}')}
        }
        
        //combine all query elements to a String and return it
        return formatJQueryCallList(resultArray)
    }
}
